#include <stdio.h>

#include "codegenerator.h"
#include "exceptions.h"

string printSymbolCode(ParseTree::Name n, Symbol s);
string printNameCode(string loc, ParseTree::Name n);
string printNameList(string var, ParseTree::Name ns, vector<ParseTree::Name> &names);
string printNameList(string var, ParseTree::Name ns, vector<ParseTree::Type> &names);

SymbolTable modSymbolTable;

bool isPublic(SymbolTable &s, ParseTree::Name &name)
{
    for (unsigned int i = 0; i < s[name].modifiers.size(); i++)
        if (s[name].modifiers[i] == ParseTree::Public)
        {
            // symbol is defined public. Now checking if all it's parents are public
            ParseTree::Name newName = name;
            newName.pop_back();
            if (newName.size() == 0 || s.find(newName) == s.end())
            {
                // newName is not in the symboltable
                return true;
            }
            return isPublic(s, newName);
        }
    return false;
}

string generateModuleCode(ParseTree::Thinder &thinder, SymbolTable &s, OperatorMap &m)
{
    modSymbolTable = s;
    string code = "#include \"Common/include/symboltable.h\"\n"; // TODO: other way to figure out the location
    //string code = "#include <thinder/symboltable.h>\n";
    
    code += "struct SymbolTable *t_int_getTable() {\n";
    code += "struct SymbolTable *table = malloc(sizeof(struct SymbolTable));\nstruct SymbolTable *next = 0;\n";
    code += "next = table;";
    
    SymbolTable::iterator it;
    bool hasPublics = false;
    bool first = true;
    for (it = s.begin(); it != s.end(); it++)
    {
        ParseTree::Name name = (*it).first;
        if (isPublic(s, name))
        {
            hasPublics = true;
            if (!first) 
            {
                code += "next->next = malloc(sizeof(struct SymbolTable));\n";
                code += "next = next->next;\n";
            }
            else first = false;
            code += printSymbolCode((*it).first, (*it).second) + "\n";
            
            code += "next->next = 0;\n";
        }
    }
        
    code += "return table;\n}\n";
    
    if (!hasPublics)
    {
        code = "#include \"Common/include/symboltable.h\"\nstruct SymbolTable *t_int_getTable() {\nreturn 0;\n}\n";
    }
    
    // DEPENDENCIES
    code += "\nstruct NameList *t_int_getDepend() {\n";
    code += "struct NameList *names;\n";
    code += printNameList("names", thinder.ns, thinder.imports);
    code += "return names;\n";
    code += "}\n";
    
    return code;
}

string printSymbolCode(ParseTree::Name n, Symbol s)
{
    string str = "next->symbolType = ";
    if (s.symbolType == Symbol::Var)
        str += "Var;\n";
    if (s.symbolType == Symbol::Func)
        str += "Func;\n";
    if (s.symbolType == Symbol::Class)
        str += "Class;\n";
    if (s.symbolType == Symbol::Operator)
        str += "Operator;\n";
    if (s.symbolType == Symbol::Constructor)
        str += "Constructor;\n";
    str += "strcpy(next->internal, \"" + s.internal + "\");\n";
    str += "next->modifiers = 0 | ";
    for (unsigned int i = 0; i < s.modifiers.size(); i++)
    {
        switch (s.modifiers[i])
        {
            case ParseTree::Public:
                str += "FLAG_PUBLIC";
                break;
            case ParseTree::Protected:
                str += "FLAG_PROTECTED";
                break;
            case ParseTree::Private:
                str += "FLAG_PRIVATE";
                break;
            case ParseTree::Static:
                str += "FLAG_STATIC";
                break;
            case ParseTree::Abstract:
                str += "FLAG_ABSTRACT";
                break;
            case ParseTree::Final:
                str += "FLAG_FINAL";
                break;
        }
        str += " | ";
    }
    str += "0;\n";
    
    if (s.symbolType == Symbol::Func || s.symbolType == Symbol::Constructor)
    {
       if (s.argumentTypes.size() > 0)
           str += printNameList("next->argumentTypes", n, s.argumentTypes);
    }
    
    str += printNameCode("next->name", n);
    str += printNameCode("next->type", getName(n, s.type));
    
    return str;
}

string printNameCode(string loc, ParseTree::Name n)
{
    string str = "";
    
    for (unsigned int i = 0; i < n.size(); i++)
    {
        str += loc;
        for (unsigned int j = 0; j < i; j++)
            str += "->next";
        str += " = malloc(sizeof(struct Name));\n";
    }
    
    for (unsigned int i = 0; i < n.size(); i++)
    {
        str += "strcpy(" + loc;
        for (unsigned int j = 0; j < i; j++)
            str += "->next";
        str += "->identifier, \"" + n[i].identifier + "\");\n";
    }
    
    str += loc;
    for (unsigned int i = 0; i < n.size(); i++)
        str += "->next";
    str += " = 0;\n";
    
    return str;
}

ParseTree::Name getName(ParseTree::Name ns, ParseTree::Type type)
{
    if (type.typeType == ParseTree::Type::UserType)
        return namespaceLookupFQN(modSymbolTable, ns, type.userType);
    else
    {
        switch (type.primitiveType)
        {
            case ParseTree::Type::Int:
                return toName("int");
                break;
            case ParseTree::Type::Float:
                return toName("float");
                break;
            case ParseTree::Type::Bool:
                return toName("bool");
                break;
            case ParseTree::Type::Byte:
                return toName("byte");
                break;
            case ParseTree::Type::Char:
                return toName("char");
                break;
            case ParseTree::Type::Long:
                return toName("long");
                break;
            case ParseTree::Type::Double:
                return toName("double");
                break;
            case ParseTree::Type::Short:
                return toName("short");
                break;
        }
    }
    return toName("void");
}

string printNameList(string var, ParseTree::Name ns, vector<ParseTree::Type> &names)
{
    string str = "";
    
    for (unsigned int i = 0; i < names.size(); i++)
    {
        str += var;
        for (unsigned int j = 0; j < i; j++)
            str += "->next";
        str += " = malloc(sizeof(struct NameList));\n";
    }
    
    for (unsigned int i = 0; i < names.size(); i++)
    {
        string str2 = "";
        str2 += var;
        for (unsigned int j = 0; j < i; j++)
            str2 += "->next";
        str += printNameCode(str2 + "->name", getName(ns, names[i]));
    }
    
    str += var;
        for (unsigned int j = 0; j < names.size(); j++)
            str += "->next";
    str += " = 0;\n";
    
    
    return str;
}

string printNameList(string var, ParseTree::Name ns, vector<ParseTree::Name> &names)
{
    string str = "";
    
    for (unsigned int i = 0; i < names.size(); i++)
    {
        str += var;
        for (unsigned int j = 0; j < i; j++)
            str += "->next";
        str += " = malloc(sizeof(struct NameList));\n";
    }
    
    for (unsigned int i = 0; i < names.size(); i++)
    {
        string str2 = "";
        str2 += var;
        for (unsigned int j = 0; j < i; j++)
            str2 += "->next";
        str += printNameCode(str2 + "->name", names[i]);
    }
    
    str += var;
        for (unsigned int j = 0; j < names.size(); j++)
            str += "->next";
    str += " = 0;\n";
    
    
    return str;
}


